﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Cofe.Core.Interfaces;

namespace Cofe.Core.Property
{
    public class PropertyCacheDictionary : IPropertyCacheDictionary
    {
        #region Constructor

        public PropertyCacheDictionary()
        {

        }

        #endregion

        #region Methods

        private void updateCacheProperties(PropertyPair ppair)
        {
            switch ((int)ppair.Property)
            {
                case (int)CacheProperties.Id:
                    _entryId = ppair.ValueAs<Guid>();
                    break;

                case (int)CacheProperties.SupportedProperties:
                    _supportedProperties = new List<object>(ppair.ValueAs<object[]>());
                    break;

                case (int)CacheProperties.LastCachedTimeUtc:
                    _lastUpdateTime = ppair.ValueAs<DateTime>();
                    break;

                case (int)CacheProperties.SupportedDefinitions:
                    _supportedDefinitions = ppair.ValueAs<IPropertyDefinitions[]>();
                    break;
            }

            switch ((int)ppair.Property)
            {
                case (int)CacheProperties.SupportedProperties:
                case (int)CacheProperties.SupportedDefinitions:
                    if (_supportedProperties != null && _supportedDefinitions != null)
                        _supportedDefinitions =
                            CofeServices.PropertyDictionary.CreatePropertyDefinitions(
                            _supportedProperties, _supportedDefinitions);
                    break;
            }

           
            
        }

        public void CacheProperty(PropertyPair ppair)
        {
            updateCacheProperties(ppair);

            _cacheDic[ppair.Property] = ppair.Value;
            _lastUpdateTime = DateTime.UtcNow;

        }

        public void SetProperty(PropertyPair ppair)
        {
            updateCacheProperties(ppair);
            _changedDic[ppair.Property] = ppair.Value;
            _lastUpdateTime = DateTime.UtcNow;
        }

        public PropertyPair GetProperty(object property)
        {
            switch ((int)property)
            {
                case (int)CacheProperties.Id:
                    return PropertyPair.FromValue(property, _entryId);

                case (int)CacheProperties.SupportedProperties:
                    return PropertyPair.FromValue(property, GetSupportedProperties().ToArray());

                case (int)CacheProperties.CachedProperties:
                    return PropertyPair.FromValue(property, GetCachedProperties().ToArray());

                case (int)CacheProperties.ChangedProperties:
                    return PropertyPair.FromValue(property, GetChangedProperties().ToArray());

                case (int)CacheProperties.LastCachedTimeUtc:
                    return PropertyPair.FromValue(property, _lastUpdateTime);

                default:
                    if (_changedDic.ContainsKey(property))
                        return PropertyPair.FromValue(property, _changedDic[property]);
                    else if (_cacheDic.ContainsKey(property))
                        return PropertyPair.FromValue(property, _cacheDic[property]);
                    else throw new NotSupportedException();
            }
        }

        public void ClearDictionary(bool changed, bool cache, Func<object, bool> filter = null)
        {
            Action<ConcurrentDictionary<object, object>> checkDic = (dic) =>
                {
                    foreach (var prop in dic.Keys.ToArray())
                        switch ((int)prop)
                        {
                            case (int)CofeProperties.FullParseName:
                            case (int)CofeProperties.ParentParseName:
                            case (int)CacheProperties.Id:
                            case (int)CacheProperties.ParentId:
                            case (int)CofeEntryTypeProperties.EntryTypeInfo:
                            case (int)CofeProperties.DefaultInterfaceType:
                            case (int)CofeProperties.DefaultInterfaceTypeString:
                            case (int)CofeProperties.Type:
                                break; //These cannot be resetted.
                            default:
                                if (filter == null || filter(prop))
                                {
                                    object value;
                                    if (dic.ContainsKey(prop))
                                        dic.TryRemove(prop, out value);
                                }
                                break;
                        }


                };


            if (changed) checkDic(_changedDic);
            if (cache) checkDic(_cacheDic);
        }

        public IEnumerable<object> GetChangedProperties() { return _changedDic.Keys; }

        public IEnumerable<object> GetCachedProperties()
        {
            foreach (var k in _cacheDic.Keys)
                yield return k;

            if (_entryId != Guid.Empty)
                yield return CacheProperties.Id;

            yield return CacheProperties.CachedProperties;
            yield return CacheProperties.ChangedProperties;
            yield return CacheProperties.LastCachedTimeUtc;

            if (_supportedProperties != null && _supportedProperties.Count > 0)
                yield return CacheProperties.SupportedProperties;

        }

        public IEnumerable<object> GetSupportedProperties()
        {
            foreach (var p in _supportedProperties)
                yield return p;
            foreach (var p in GetCachedProperties())
                yield return p;

        }

        public IPropertyDefinitions[] GetSupportedDefinitions(IEnumerable<object> additionalProperties = null)
        {
            if (additionalProperties == null)
                return _supportedDefinitions ?? CofeServices.PropertyDictionary.CreatePropertyDefinitions(
                GetSupportedProperties());

            return _supportedDefinitions ?? CofeServices.PropertyDictionary.CreatePropertyDefinitions(
                GetSupportedProperties().Union(additionalProperties));
        }

        public IEnumerable<object> GetPropertiesInDic()
        {
            return GetChangedProperties().Union(GetCachedProperties()).Distinct();
            //yield return CacheProperties.Id;
            //yield return CacheProperties.SupportedProperties;
            //yield return CacheProperties.CachedProperties;
            //yield return CacheProperties.LastCachedTimeUtc;
            //foreach (var k in _changedDic.Keys) yield return k;
            //foreach (var k in _cacheDic.Keys) yield return k;        
        }

        #endregion

        #region Data

        private ConcurrentDictionary<object, object> _changedDic = new ConcurrentDictionary<object, object>();
        private ConcurrentDictionary<object, object> _cacheDic = new ConcurrentDictionary<object, object>();

        private Guid _entryId = Guid.Empty;
        private DateTime _lastUpdateTime = DateTime.UtcNow;
        private List<object> _supportedProperties = new List<object>();
        private IPropertyDefinitions[] _supportedDefinitions = null;

        #endregion

        #region Public Properties


        #endregion
    }
}
